<?php

$GLOBALS['objects']['database_db']= null;
$GLOBALS['objects']['database_logdb']= null;

class Database {
	private $connction = FALSE;
	private $transaction_in_progress = FALSE;
	public $query_num = 0; //查询计数器
	private $last_errmsg; 
	private $last_errno;

	/**
	 * 操作数据的基础类
	 *
	 * @return null
	 */
	private function __construct($db_link = 'db') {
		$config = Context::getConfig($db_link);

		if($config['sock']){
			$this->connection	= new mysqli($config['host'], $config['user'], $config['password'], $config['database'],$config['port'],$config['sock']);
		}else{
			$this->connection	= new mysqli($config['host'], $config['user'], $config['password'], $config['database'],$config['port']);
		}
		/* check connection */
		if ($this->connection === false || $this->connection->connect_error) {
			printf("Connect failed: %s\n", mysqli_connect_error());
			exit();
		}

		$this->connection->set_charset("utf8");
	}

	/**
	 * 取得数据库操作实例
	 *
	 * @return Database  一次脚本运行中的单例
	 */
	public static function & getInstance($db_link = 'db') {
		$key = 'database_'.$db_link;
		if(!isset($GLOBALS['objects'][$key]) || is_null($GLOBALS['objects'][$key])) {
			$db = new Database($db_link);
			$GLOBALS['objects'][$key] = $db;
		}
		return $GLOBALS['objects'][$key];
	}

	/**
	 * 查询一个单一值，如每个人的姓名
	 *
	 * @param string $sql 需要执行的sql
	 * @param mix $val1  参数1
	 * @param mix $val2  参数2，依次...
	 *
	 * @return mix 单一的值
	 */
	public function queryValue() {
		$args = func_get_args();
		$sql = array_shift($args);
		//add limit 1 when it does not exist limit synatx
		if(stripos($sql, 'limit') === FALSE)
		$sql .= ' limit 1';
			
		$result = $this->_query($sql, $args);
		if($result && is_array($result) && count($result) > 0) {
			$result = array_shift($result[0]);
		} else {
			$result = null;
		}
		return $result;
	}

	/**
	 * 查询一行记录
	 *
	 * @param string $sql 需要执行的sql
	 * @param mix $val1  参数1
	 * @param mix $val2  参数2，依次...
	 *
	 * @return array 保持key-value
	 */
	public function queryOne() {
		$args = func_get_args();
		$sql = array_shift($args);
		//add limit 1 when it does not exist limit synatx
		if(stripos($sql, 'limit') === FALSE)
		$sql .= ' limit 1';
		$result = $this->_query($sql, $args);
		if($result && is_array($result) && count($result) > 0) {
			$result = $result[0]; //array_shift($result);
		} else {
			$result = null;
		}
		return $result;
	}

	/**
	 * 查询, 且仅能查询
	 *
	 * @param string $sql 需要执行的sql
	 * @param mix $val1  参数1
	 * @param mix $val2  参数2，依次...
	 *
	 * @return array 保持key-value
	 */
	public function query() {
		$args = func_get_args();
		$sql = array_shift($args);
		$result = $this->_query($sql, $args);
		return $result;
	}

	/**
	 * 实际查询的执行方法
	 *
	 * @param string $sql  需要执行的sql
	 * @param array $paraArray， 参数形成的数组
	 * @return mix  返回array(array(), array())形式
	 */
	public function _query($sql, $paraArray = null) {
		$stmt = $this->prepare($sql);
		$this->bindParam($stmt, $paraArray);
		$stmt->execute();

		$meta = $stmt->result_metadata();
		while ($field = $meta->fetch_field())
		{
			$params[] = &$row[$field->name];
		}
		call_user_func_array(array($stmt, 'bind_result'), $params);
		$result = array();
		while ($stmt->fetch()) {
			foreach($row as $key => $val)
			{
				$c[$key] = $val;
			}
			$result[] = $c;
		}

		$stmt->close();

		return $result;
	}

	/**
	 * 执行更新或删除动作
	 *
	 * @param string $sql 需要执行的sql
	 * @param mix $val1  参数1
	 * @param mix $val2  参数2，依次...
	 *
	 * @return int 更新操作过程中影响的记录数目
	 */
	public function execute()
	{
		$this->query_num++;
		$args = func_get_args();
		$sql = array_shift($args);
		$stmt = $this->prepare($sql);
		$this->bindParam($stmt, $args);
		$stmt->execute();
		
		
		$this->last_errmsg = $this->connection->error;
		$this->last_errno = $this->connection->errno;
		$affected_rows = $stmt->affected_rows;
		$stmt->close();
		return $affected_rows;
	}

	/**
	 * 执行更新或删除动作
	 *
	 * @param string $sql 需要执行的sql
	 * @param mix $val  参数，数组形式
	 *
	 * @return int 更新操作过程中影响的记录数目
	 */
	public function run($sql,$args)
	{
		$stmt = $this->prepare($sql);
		$re = false;
		if ($stmt)
		{
			if ($this->bindParam($stmt, $args))
			{
				$re = $stmt->execute();
				$re = $stmt->affected_rows;
			}
			else
			{
				echo 'SQL BindParam Error!<br>';
				echo "sql has problems: ".$sql;
				echo "<br>\n".print_r($args,1);
				echo "<br>\nError:";
				echo $this->connection->error;
				throw new Exception("SQL Error");
				die;
			}
			$stmt->close();
		}
		else
		{
			echo 'SQL Prepare Error!';
			die;
		}
		//$affected_rows = $stmt->affected_rows;
		if ($re < 0) $re = false;
		return $re;
	}

	/**
	 * 批量执行
	 *
	 * @param string $sql 插入语句
	 * @param array $dataArray 值的数组， array(array(), array(), ...)
	 * @return int 成功插入的数目
	 */
	public function batchExecute($sql, $dataArray) {
		$stmt = $this->prepare($sql);
		$affected_rows = 0;
		foreach($dataArray as $data) {
			$this->bindParam($stmt, $data);
			$stmt->execute();
			$affected_rows += $stmt->affected_rows;
		}

		$stmt->close();
		return $affected_rows;
	}

	/**
	 * 获取最近一次插入操作所产生的id (自增类型)
	 *
	 * @return int 自增id	 
	 */
	public function getInsertId() {
		return $this->connection->insert_id;
	}

	/**
	 * 开启事务支持
	 *
	 * @return null
	 */
	public function begin_transaction() {
		$ret = $this->connection->autocommit(false);
		$this->transaction_in_progress = true;
	}

	/**
	 * 提交事务
	 *
	 * @return null
	 */
	public function commit() {
		$ret = $this->connection->commit();
		$this->transaction_in_progress = false;
	}

	/**
	 * 回滚事务
	 *
	 * @return null
	 */
	public function rollback() {
		$ret = $this->mysqli_obj->rollback();
		$this->transaction_in_progress = false;
	}

	/**
	 * 获取数据库最后的一个错误信息
	 * @return str
	 */
	public function getLastError() {
		return $this->last_errmsg ? $this->last_errmsg :$this->connection->error;
		//return $this->connection->error;
	}
	public function getLastErrorNo(){
		return $this->last_errno ? $this->last_errno :$this->connection->errno;
		//return $this->connection->errno;
	}
	private function &prepare($sql) {
		$stmt = $this->connection->prepare($sql);
		if($stmt === FALSE)
		{
			echo "sql has problems: ".$sql;
			echo "<br>\nError:";
			echo $this->connection->error;
			throw new Exception("SQL Error");
			die;
		}
		
		return $stmt;
	}
	/**
	 * 绑定参数
	 *
	 * @param statement $stmt
	 * @param array $args
	 * @return null
	 */
	private function bindParam(&$stmt, &$args) {
		if(is_null($args) || count($args) == 0 )
		return false;

		$types = "";
		foreach($args as $var) {
			$type = 's';
			if (is_int($var)) {
				$type = 'i';
			}
			else if (is_double($var)) {
				$type = 'd';
			}
			//			else if (is_string($var)) {
			//				$var = $this->connection->real_escape_string($var);
			//			}
			$types .= $type;
			$paras[] = $var;
		}
		array_unshift($paras,$types);
		$tmp = array();
		foreach($paras as $key => $value) $tmp[$key] = &$paras[$key];
		$result = call_user_func_array(array($stmt, 'bind_param'), $tmp);
		unset($paras);
		
		return $result;
	}

	public function __destruct()
	{
		if(FALSE !== $this->connection) {
			if ($this->transaction_in_progress) {
				$this->rollback();
			}
			$this->connection->close();
			//@mysql_close($this->connection);
			$this->connection	= FALSE;
		}
	}
}
